﻿Imports System.Web.Configuration

''' <summary>
''' Modifies the remote access login page to hit against the yubikey web service.
''' </summary>
''' <remarks>This inserts the yubikey validation client code contained in Script.txt into
''' the login.aspx page for the remote access logon.aspx page. It first backs up the existing
''' logon.aspx so that this can be reverted to later. My initial thought was to just create a different
''' aspx, and redirect to that using the web.config, but try to use the same code behind, but there
''' appear to be references to redirecting to logon.aspx in the code behind.</remarks>
Public Class LoginRedirector
    'TODO: do some home server installations put the remote access site elsewhere? investigate.
    Private m_WebsiteLocation As String = "C:\inetpub\remote"

    ''' <summary>
    ''' The location to backup the existing logon page to, before modifying the page.
    ''' </summary>
    ''' <remarks></remarks>
    Private m_BackupLogon As String

    ''' <summary>
    ''' The location of the current login page.
    ''' </summary>
    ''' <remarks></remarks>
    Private m_OldLogon As String

    ''' <summary>
    ''' The location to which to copy the Yubiverifier class library.
    ''' </summary>
    ''' <remarks></remarks>
    Private m_SelfCopy As String

    ''' <summary>
    ''' The starting location of the Yubiverifier class library.
    ''' </summary>
    ''' <remarks></remarks>
    Private m_SelfSource As String
    Private m_YubiUrl As String
    Private m_YubiAppKey As String
    Private m_YubiAppId As String

    ''' <summary>
    ''' The url for the yubikey verification web service
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Url() As String
        Get
            Return m_YubiUrl
        End Get
        Set(ByVal value As String)
            m_YubiUrl = value
        End Set
    End Property

    ''' <summary>
    ''' The shared key to sign and verify signartures for messages exchanged with the yubikey service.
    ''' </summary>
    ''' <value></value>
    ''' <returns>This is related to the appid, the verification server will use the app key that matches the app id
    ''' you provide when making requests to the service.</returns>
    ''' <remarks></remarks>
    Public Property AppKey() As String
        Get
            Return m_YubiAppKey
        End Get
        Set(ByVal value As String)
            m_YubiAppKey = value
        End Set
    End Property

    ''' <summary>
    ''' The app id for this client of the yubikey validation service.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property AppID() As String
        Get
            Return m_YubiAppId
        End Get
        Set(ByVal value As String)
            m_YubiAppId = value
        End Set
    End Property

    Private m_Deployed As Boolean = False
    ''' <summary>
    ''' Are we currently deployed.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Deployed() As Boolean
        Get
            Return m_Deployed
        End Get
        Set(ByVal value As Boolean)
            m_Deployed = value
        End Set
    End Property

    Private m_SettingsFileName As String
    ''' <summary>
    ''' The location that the settings should be persisted or loaded from.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property SettingsFileName() As String
        Get
            Return m_SettingsFileName
        End Get
        Set(ByVal value As String)
            m_SettingsFileName = value
        End Set
    End Property

    Public Sub New(ByVal pYubiUrl As String, ByVal pYubiAppKey As String, ByVal pYubiAppId As String, ByVal pSettingsFileName As String)
        m_YubiUrl = pYubiUrl
        m_YubiAppKey = pYubiAppKey
        m_YubiAppId = pYubiAppId
        m_SettingsFileName = pSettingsFileName
        'we will backup the existing login.aspx to the bin folder as asp.net will mask this, and disallow navigation to the page.
        m_BackupLogon = IO.Path.Combine(IO.Path.Combine(m_WebsiteLocation, "bin"), "logon.aspx")
        'the location of the current logon.aspx page.
        m_OldLogon = IO.Path.Combine(m_WebsiteLocation, "logon.aspx")
        m_SelfCopy = IO.Path.Combine(IO.Path.Combine(m_WebsiteLocation, "bin"), "YubiVerifier.dll")
        m_SelfSource = IO.Path.Combine(IO.Path.GetDirectoryName(Reflection.Assembly.GetExecutingAssembly.Location), "YubiVerifier.dll")
    End Sub

    ''' <summary>
    ''' Ensure that we have copied a backup of the logon.aspx to the bin folder for safe-keeping.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub CheckLogonBackedUp()
        If Not IO.File.Exists(m_BackupLogon) Then
            IO.File.Copy(m_OldLogon, m_BackupLogon)
        End If
    End Sub

    ''' <summary>
    ''' Inject our code changes into the code from the existing logon.aspx.
    ''' </summary>
    ''' <param name="pPage"></param>
    ''' <returns></returns>
    ''' <remarks>We need a new textbox for the one time password, and to augment the events on the submit button.</remarks>
    Public Function ModifyLogonPage(ByVal pPage As String) As String
        Dim _inject As New Text.StringBuilder
        Dim _toReplace As String = "<td><uc1:QWebTextbox ID=""txtPassword"" Mode=""Password"" runat=""server"" CssClass=""LogonPagePasswordBox"" />"
        _inject.AppendLine(_toReplace)
        _inject.AppendLine("</td></tr>")
        _inject.AppendLine("<tr>")
        _inject.AppendLine("<td><uc1:QWebTextbox ID=""txtOTPassword"" Mode=""Password"" runat=""server"" CssClass=""LogonPagePasswordBox"" />")
        pPage = pPage.Replace(_toReplace, _inject.ToString)

        Dim _scriptInject As New Text.StringBuilder
        _toReplace = "</script>"
        _scriptInject.AppendLine("</script>")
        _scriptInject.AppendLine("<script runat=""server"">")
        Dim _streamReader As New IO.StreamReader(Reflection.Assembly.GetExecutingAssembly.GetManifestResourceStream("TwoFactorWHS.Script.txt"))
        _scriptInject.AppendLine(_streamReader.ReadToEnd.Replace("@@Replace@@", m_SettingsFileName))
        _scriptInject.AppendLine("</script>")
        pPage = pPage.Replace(_toReplace, _scriptInject.ToString)

        Return pPage
    End Function

    ''' <summary>
    ''' Modifies the logon.aspx with our injected code.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub CreateNewLogonFile()
        Dim _text As String = IO.File.ReadAllText(m_OldLogon)
        _text = ModifyLogonPage(_text)
        IO.File.WriteAllText(m_OldLogon, _text)
    End Sub

    ''' <summary>
    ''' copy the yubiverifier class library to the bin folder for the remote access site.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub DeploySelfToBin()
        IO.File.Copy(m_SelfSource, m_SelfCopy)
    End Sub

    ''' <summary>
    ''' remote the yubiverifier class library from the bin folder of the remote access site.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub RemoveSelfFromBin()
        IO.File.Delete(m_SelfCopy)
    End Sub

    ''' <summary>
    ''' restore the backup that we took of the logon.aspx before modification.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub RevertLogon()
        IO.File.Copy(m_BackupLogon, m_OldLogon, True)
        IO.File.Delete(m_BackupLogon)
    End Sub

    ''' <summary>
    ''' Perform the logon modification process.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub RedirectLogon()
        CheckLogonBackedUp()
        CreateNewLogonFile()
        DeploySelfToBin()
    End Sub

    ''' <summary>
    ''' Revert back to the original setup.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub UnRedirectLogon()
        RevertLogon()
        RemoveSelfFromBin()
    End Sub
End Class
